/* Soot - a J*va Optimization Framework
 * Copyright (C) 1999-2010 Hossein Sadat-Mohtasham
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
package soot.toolkits.graph.pdg;


import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import soot.SootClass;
import soot.SootMethod;
import soot.Unit;
import soot.toolkits.graph.Block;
import soot.toolkits.graph.UnitGraph;


/**
 * This class was originally designed to represent a weak region. Later,
 * PDGRegion was designed to represent a richer region representation but 
 * since there were cases that we wanted to enforce the use of Region instead
 * of PDGRegion, and for some other compatibility issues, we chose not to eliminate
 * this class and even not to factor it into a common abstract class. 
 * 
 * One major contributing factor to the above is that, a Region can exist 
 * independent from a PDG since they are generated by RegionAnalysis, whereas
 * a PDGRegion is generated by a PDG.
 * 
 * @author Hossein Sadat-Mohtasham
 * Jan 2009
 */

public class Region implements IRegion{
	
	private SootClass m_class = null;;
	private SootMethod m_method = null;
	private List<Block> m_blocks = null;
	private List<Unit> m_units = null;
	private int m_id = -1;
	private UnitGraph m_unitGraph = null;
	
	//The following are needed to create a tree of regions based on the containment (dependency)
	//relation between regions.
	private IRegion m_parent = null;
	private List<IRegion> m_children = new ArrayList<IRegion>();
	
	
	public Region(int id, SootMethod m, SootClass c, UnitGraph ug)
	{
		this(id, new ArrayList<Block>(), m, c, ug);
		
	}
	
	public Region(int id, List<Block> blocks, SootMethod m, SootClass c, UnitGraph ug)
	{
		
		this.m_blocks = blocks;
		this.m_id = id;
		this.m_method = m;
		this.m_class = c;
		this.m_unitGraph = ug;
		this.m_units = null;
		
		
	}
		
	@SuppressWarnings("unchecked")
	public Object clone()
	{
		Region r = new Region(this.m_id, this.m_method, this.m_class, this.m_unitGraph);
		r.m_blocks = (List<Block>)((ArrayList<Block>)this.m_blocks).clone();
		
		return r;
		
	}
	public SootMethod getSootMethod()
	{
		return this.m_method;
	}
	
	public SootClass getSootClass()
	{
		return this.m_class;
	}
	
	public List<Block> getBlocks()
	{
		return this.m_blocks;
	}
	
	
	public UnitGraph getUnitGraph()
	{
		return this.m_unitGraph;
	}
	public List<Unit> getUnits()
	{
		if(this.m_units == null)
		{
			this.m_units = new LinkedList<Unit>();
			for(Iterator<Block> itr = this.m_blocks.iterator(); itr.hasNext();)
			{
				Block b = itr.next();
				for(Iterator<Unit> itr1 = b.iterator(); itr1.hasNext();)
				{
					Unit u = itr1.next();
					((LinkedList<Unit>)this.m_units).addLast(u);

				}
			}
			
		}
		
		return this.m_units;
	}
	
	public List<Unit> getUnits(Unit from, Unit to)
	{
		
		return m_units.subList(m_units.indexOf(from), m_units.indexOf(to));
		
	}
	
	public Unit getLast()
	{
		if(this.m_units != null)
			if(this.m_units.size() > 0)
				return ((LinkedList<Unit>)this.m_units).getLast();
			
				
		return null;
	}
	
	public Unit getFirst()
	{
		if(this.m_units != null)
			if(this.m_units.size() > 0)
				return ((LinkedList<Unit>)this.m_units).getFirst();
		
		
		return null;
	}

	
	
	public void add(Block b)
	{
		//Add to the front
		this.m_blocks.add(0, b);
		
	}
	
	public void add2Back(Block b)
	{
		//Add to the last
		this.m_blocks.add(this.m_blocks.size(), b);
		
	}
	
	public void remove(Block b)
	{
		this.m_blocks.remove(b);
		//make the units be recalculated.
		this.m_units = null;
	}
	
	public int getID()
	{
		return this.m_id;
	}
	
	public boolean occursBefore(Unit u1, Unit u2)
	{
		int i = this.m_units.lastIndexOf(u1);
		int j = this.m_units.lastIndexOf(u2);
		
		if(i == -1 || j == -1)
			throw new RuntimeException("These units don't exist in the region!");
		
		return i < j;
	}
	
	
	public void setParent(IRegion pr)
	{
		this.m_parent = pr;
	}
	public IRegion getParent()
	{
		return this.m_parent;
	}
	
	public void addChildRegion(IRegion chr)
	{
		if(!this.m_children.contains(chr))
			this.m_children.add(chr);
	}
	
	public List<IRegion> getChildRegions()
	{
		return this.m_children;
	}
	public String toString()
	{
		String str = new String();
		str += "Begin-----------Region:  " + this.m_id + "-------------\n";
		
		List<Unit> regionUnits = this.getUnits();		
		for (Iterator<Unit> itr = regionUnits.iterator(); itr.hasNext();) 
		{
			Unit u = itr.next();
			str += u + "\n";
			
		}
		str += "End Region " + this.m_id + " -----------------------------\n";
		
		return str;
		
	}
	
	
}
